//Realiza la evalucacion de codigo evitando la
//mayor cantidad de variables en cierres posibles
if (!Function.prototype.bind) {
    Function.prototype.bind = function (oThis) {
        if (typeof this !== "function") {
            // closest thing possible to the ECMAScript 5 internal IsCallable function
            throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
        }

        var aArgs = Array.prototype.slice.call(arguments, 1),
            fToBind = this,
            fNOP = function () {},
            fBound = function () {
                return fToBind.apply(this instanceof fNOP && oThis ? this : oThis,
                    aArgs.concat(Array.prototype.slice.call(arguments)));
            };

        fNOP.prototype = this.prototype;
        fBound.prototype = new fNOP();

        return fBound;
    };
}

//Carga less y le suma funcionalidad

(function () {

    function require(path) {
        
        var xhr = ("XMLHttpRequest" in window) ? new XMLHttpRequest(): new ActiveXObject("Microsoft.XMLHTTP");
        xhr.open("GET", path, false);
        xhr.send(null);
        eval(xhr.responseText);
    }
    require("tools/BrowserDetect.js");

    if(BrowserDetect.browser == "Explorer" && BrowserDetect.version < 9) {
        
        location.href = "Requirements.html";
        window.forceexit = true;
    }
    
    require("tools/less-1.3.3.min.js");
    less.env = 'development';


    require("tools/rsh.compressed.js");
    
    



    var loaded = [];
    less.load = function (url) {
        if (loaded.indexOf(url) != -1) {
            return;
        }

        var link = document.createElement("link");
        link.setAttribute("rel", "stylesheet/less");
        link.setAttribute("type", "text.css");
        link.setAttribute("href", url);
        document.getElementsByTagName("head")[0].appendChild(link);
        less.sheets.push(link);
        less.refresh();

        loaded.push(url);
    }

    xhr = null;



}());





function signal() {
    "use strict";
    var cb = [];

    var rv = function (args) {
        for (var i = 0; i < cb.length; i++) {
            try {
                cb[i](this, args);
            } catch (ex) {
                debugger;
                console.error(ex);
            }
        }
    }

    rv.add = function (f) {
        cb.push(f);
    }

    rv.remove = function (f) {
        cb.splice(cb.indexOf(f), 1);
    }

    return rv;
}

function EVAL(__internals__) {

    (function () {


        function base(previousThis, previousArguments) {
            return previousArguments.callee.__internals__.base.bind(previousThis);
        }

        function inherits(cls) {
            __internals__.baseclass = cls;
        }

        function using($0) {
            for (var $1 in $0) {
                eval($1 + " = $0." + $1);
            }
        }

        using(__internals__.copy);

        eval(__internals__.source);


        for (var i in __internals__.names) {
            __internals__.methods[__internals__.names[i]] = eval(__internals__.names[i])
        }



    }());
}

(function () {
    //Clase de eventos generica

    if(window.forceexit)
        return;





    //Crea una clase
    //Se especifica un nombre y un callback que obtiene su constructor y metodos
    function makeClass(name, load) {
        "use strict";

        var init = function (self) {
            if (load.call)
                load = load();
            for (var i in load.methods)
                self[i] = load.methods[i].bind(self);
            self.$ = new(function ImProtectedBeCarefull() {})();
        }
        var rv = (function () {
            return eval("[function " + name + "() {\n    init(this);\n    load.constructor.apply(this,[].slice.call(arguments));\n}][0]")
        }());
        rv.getMethods = function () {
            if (load.call)
                load = load();
            var rv = {
                methods: {}
            };
            for (var i in load.methods) {
                rv.methods[i] = load.methods[i];
            }
            rv.constructor = load.constructor;
            return rv;
        }
        return rv;
    }

    function requestText(url) {

        var xhr = ("XMLHttpRequest" in window) ? new XMLHttpRequest(): new ActiveXObject("Microsoft.XMLHTTP");
        xhr.open("GET", url, false);
        xhr.send(null);
        return xhr.responseText
    }


    function getManifest(url) {
        "use strict";
        var aux = requestText(url).split("\n");
        var rv = [];
        for (var i = 0; i < aux.length; i++)
            rv.push(aux[i].split(" "));
        return rv;
    }




    var main = getManifest("Config.txt")[0][1];


    function createClassLoader(name, filename, copy) {
        "use strict";
        return makeClass(name, function () {
            var xhr = new XMLHttpRequest();


            var source = requestText(filename);


            xhr = null;
            var names = getInnerFunctionNames(source);


            var data = {
                source: source,
                names: names,
                methods: {},
                copy: copy
            };

            EVAL(data);

            var rv = {
                methods: {}
            };

            if (data.baseclass) {

                var basedata = data.baseclass.getMethods();

                rv.constructor = basedata.constructor;
                for (var i in basedata.methods) {
                    rv.methods[i] = basedata.methods[i]
                }

            }


            if (!data.methods[name]) {

                //console.warn("La clase ubicada en el archivo '" + filename +"' no tiene un constructor declarado");
                if (!rv.constructor) {
                    rv.constructor = function () {}
                }

            } else {
                if (rv.constructor) {

                    data.methods[name].__internals__ = {
                        base: rv.constructor
                    };
                }
                rv.constructor = data.methods[name];
            }




            for (var i in data.methods) {
                if (i != name) {
                    if (rv.methods[i]) {
                        data.methods[i].__internals__ = {
                            base: rv.methods[i]
                        };
                    }
                    rv.methods[i] = data.methods[i];
                }
            }


            return rv;

        });
    }

    function putClasses(nsSource, nsTarget) {
        for (var i in nsTarget) {

            if (nsTarget[i] instanceof Function) {

                nsSource[i] = nsTarget[i]

            } else {

                if (!nsSource[i]) {
                    nsSource[i] = new(function Namespace() {});
                }

                putClasses(nsSource[i], nsTarget[i]);
            }


        }
    }

    var loaded = {};

    function getDSL(uid) {
        var path = uid.split(":");
        var modname = path[0];
        path = path[1].split(".");

        if (!loaded[modname])
            loadModule(modname);

        var curr = loaded[modname].classes;
        for (var i = 0; i < path.length; i++) {
            if (!curr[path[i]]) {
                throw new Error("DSL NOT FOUND!!!");
            }
            curr = curr[path[i]];
        }
        return new curr();

    }

    function loadModule(name) {

        if (loaded[name]) {
            return loaded[name];
        }

        "use strict";
        //Info real de los modulos
        var mod = {
            imports: null,
            classes: null
        };

        loaded[name] = mod;

        //Espacios de nombre finales

        var manifest = getManifest("/packages/" + name + "/Package.txt");

        //Copia superficial agregando las clases de otro modulos
        var copy = new(function Namespace() {})();

        for (var i = 0; i < manifest.length; i++) {
            if (manifest[i][0] == "class") {

                if (mod.classes == null) {
                    mod.classes = {};
                }


                var path = manifest[i][1].split(".");

                var current = mod.classes;
                for (var j = 0; j < path.length - 1; j++) {
                    var ns = current[path[j]] ? current[path[j]] : {};
                    current[path[j]] = ns;
                    current = ns;
                }

                var clsname = path[path.length - 1]

                current[clsname] = createClassLoader(clsname, "/packages/" + name + "/" + path.join("/") + ".js", copy);


            } else if (manifest[i][0] == "import") {
                if (mod.imports == null)
                    mod.imports = {};

                mod.imports[manifest[i][1]] = loadModule(manifest[i][1]);
            } else if (manifest[i][0] == "dsl") {


                var dsl = getDSL(manifest[i][1]);

                var fullid = manifest[i][2].split(".");

                var id = fullid.splice(fullid.length - 1, 1);

                fullid.push(dsl.provideFilename(id));

                fullid.unshift(name);
                fullid.unshift("packages");

                current[id] = makeClass(id, dsl.createClass("/" + fullid.join("/")));



            } else {
                throw new Error("Invalid manifest key");
            }



        }



        for (var i in mod.imports) {

            putClasses(copy, mod.imports[i].classes);

        }

        putClasses(copy, mod.classes);

        return mod;
    }






    //HERRAMIENTAS COPYPasTEADAS

    window.getInnerFunctionNames = (function () {
        function tokenize(code) {
            var code = code.split(/\\./).join(''),
                regex = /\bfunction\b|\(|\)|\{|\}|\/\*|\*\/|\/\/|"|'|\n|\s+/mg,
                tokens = [],
                pos = 0;

            for (var matches; matches = regex.exec(code); pos = regex.lastIndex) {
                var match = matches[0],
                    matchStart = regex.lastIndex - match.length;

                if (pos < matchStart)
                    tokens.push(code.substring(pos, matchStart));

                tokens.push(match);
            }

            if (pos < code.length)
                tokens.push(code.substring(pos));

            return tokens;
        }

        var separators = {
            '/*': '*/',
            '//': '\n',
            '"': '"',
            '\'': '\''
        };

        function extractInnerFunctionNames(func, nested) {
            var names = [],
                tokens = tokenize("function foo(){" + func + "}"),
                level = 0;

            for (var i = 0; i < tokens.length; ++i) {
                var token = tokens[i];

                switch (token) {
                case '{':
                    ++level;
                    break;

                case '}':
                    --level;
                    break;

                case '/*':
                case '//':
                case '"':
                case '\'':
                    var sep = separators[token];
                    while (++i < tokens.length && tokens[i] !== sep);
                    break;

                case 'function':
                    if (level === 1 || (nested && level)) {
                        while (++i < tokens.length) {
                            token = tokens[i];

                            if (token === '(')
                                break;

                            if (/^\s+$/.test(token))
                                continue;

                            if (token === '/*' || token === '//') {
                                var sep = separators[token];
                                while (++i < tokens.length && tokens[i] !== sep);
                                continue;
                            }

                            names.push(token);
                            break;
                        }
                    }
                    break;
                }
            }

            return names;
        }


        return extractInnerFunctionNames;
    }());


    var m = loadModule(main);


    window.onload = function () {


        if(BrowserDetect.browser == "Chrome") {

            window.dhtmlHistory.create({

                toJSON: function (o) {
                    return JSON.stringify(o);
                },
                fromJSON: function (s) {
                    return JSON.parse(s);
                }



            });
        } else {
            window.dhtmlHistory.add = function() { }
        }

        new m.classes.Main();


    };


    window.jsworks = new(function JsWorks() {});
    window.jsworks.resolve = function (module, id) {
        try {
            var m = loadModule(module);

            var sp = id.split(".");

            var curr = m.classes;
            for (var i = 0; i < sp.length; i++) {
                curr = curr[sp[i]];
            }
        } catch (ex) {
            throw new Error("No se puede cargar la clase '" + id + "' del modulo '" + module + "'");
        }

        return curr;

    }

}());